#include "httprequest.h"

// HttpRequest::HttpRequest():state_(REQUEST_LINE){}

const std::unordered_set<std::string> HttpRequest::DEFAULT_HTML{
    "/index",
    "/register",
    "/login",
    "/welcome",
    "/video",
    "/picture",
};

const std::unordered_map<std::string, int> HttpRequest::DEFAULT_HTML_TAG{
    {"/register.html", 0},
    {"/login.html", 1},
};

void HttpRequest::Init()
{
    method_ = path_ = version_ = body_ = "";
    state_ = REQUEST_LINE;
    header_.clear();
    post_.clear();
}

bool HttpRequest::parse(Buffer &buff)
{
    assert(buff.ReadAble() > 0);
    const char ch[] = "\r\n";

    while (buff.ReadAble() && state_ != HttpRequest::PARSE_STATE::FINISH)
    {
        const char *buffend = std::search(buff.Peek(), buff.BeginWriteConst(), ch, ch + 2);

        std::string line(buff.Peek(), buffend);


        switch (state_)
        {
        case REQUEST_LINE:
            if (!ParseRequestLine_(line))
            {
                return false;
            }
            HttpRequest::ParsePath_();
            state_ = HEADERS;
            break;
        case HEADERS:
            ParseHeader_(line);
            if (buff.ReadAble() <= 2) //
                state_ = FINISH;
            break;
        case BODY:
            ParseBody_(line);
            break;
        default:
            break;
        }

        if (buffend == buff.BeginWrite())
        {
            break;
        }
        buff.ReadUtil(buffend + 2);
    }

    LOG_DEBUG("method: [%s] ,path: [%s] ,version: [%s]", method_, path_, version_);
    return true;
}

void HttpRequest::ParsePath_()
{
    if (path_ == "/")
        path_ = "/index.html";
    else
    {
        for (auto &item : DEFAULT_HTML)
            if (item == path_)
            {
                path_ += ".html";
                break;
            }
    }
}

bool HttpRequest::ParseRequestLine_(const std::string &line)
{
    std::regex pattern("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");
    std::smatch match;
    if (std::regex_match(line, match, pattern))
    {
        method_ = match[1];
        path_ = match[2];
        version_ = match[3];
        state_ = HEADERS;
        return true;
    }
    LOG_WARN("Parse Request Line error");
    return false;
}

void HttpRequest::ParseHeader_(const std::string &line)
{
    std::regex pattern("^([^:]*) ([^ ]*)$");
    std::smatch match;
    if (std::regex_match(line, match, pattern))
    {
        header_[match[1]] = match[2];
    }
    else
    {
        state_ = BODY;
    }
}

void HttpRequest::ParseBody_(const std::string &line)
{
    body_ = line;
    ParsePost_();
    state_ = FINISH;
    LOG_DEBUG("Body:%s, len:%d", line.c_str(), line.size());
}

void HttpRequest::ParsePost_()
{
    if (method_ == "POST" && header_["Content-Type"] == "application/x-www-form-urlencoded")
    {
        // 解析表单信息
        ParseFromUrlencoded_();
        if (DEFAULT_HTML_TAG.count(path_))
        {
            int tag = DEFAULT_HTML_TAG.find(path_)->second;
            LOG_DEBUG("Tag:%d", tag);
            if (tag == 0 || tag == 1)
            {
                bool isLogin = (tag == 1);
                if (UserVerify(post_["username"], post_["password"], isLogin))
                {
                    path_ = "/welcome.html";
                }
                else
                {
                    path_ = "/error.html";
                }
            }
        }
    }
}

void HttpRequest::ParseFromUrlencoded_()
{
    if (body_.size() == 0)
    {
        return;
    }

    std::string key, value;
    int num = 0;
    int n = body_.size();
    int i = 0, j = 0;

    for (; i < n; i++)
    {
        char ch = body_[i];
        switch (ch)
        {
        case '=':
            key = body_.substr(j, i - j);
            j = i + 1;
            break;
        case '+':
            body_[i] = ' ';
            break;
        case '%':
            num = ConverHex(body_[i + 1]) * 16 + ConverHex(body_[i + 2]);
            body_[i + 2] = num % 10 + '0';
            body_[i + 1] = num / 10 + '0';
            i += 2;
            break;
        case '&':
            value = body_.substr(j, i - j);
            j = i + 1;
            post_[key] = value;
            LOG_DEBUG("%s = %s", key.c_str(), value.c_str());
            break;
        default:
            break;
        }
    }
    assert(j <= i);
    if (post_.count(key) == 0 && j < i)
    {
        value = body_.substr(j, i - j);
        post_[key] = value;
    }
}

bool HttpRequest::IsKeepAlive() const
{
    if (header_.count("Connection") == 1)
    {
        return header_.find("Connection")->second == "keep-alive" && version_ == "1.1";
    }
    return false;
}

bool HttpRequest::UserVerify(const std::string &name, const std::string &pwd, bool isLogin)
{
    if (name == "" || pwd == "")
        return false;
    MYSQL *sql = SqlConnPool::Instance()->GetConn();
    if (sql == nullptr)
    {
        LOG_ERROR("sql conn error");
        exit(0);
    }
    char order[1024] = {0};
    MYSQL_RES *result = nullptr;
    sprintf(order, "SELECT username, password FROM user WHERE username='%s' LIMIT 1", name.c_str());

    if (mysql_query(sql, order))
    {
        SqlConnPool::Instance()->FreeConn(sql);
        return false;
    }

    bool flag = false;
    if (!isLogin)
        flag = true;

    result = mysql_store_result(sql);
    MYSQL_ROW row = nullptr;
    while ((row == mysql_fetch_row(result)))
    {
        LOG_DEBUG("username: %s , passwd: %s", row[0], row[1]);
        std::string passwd(row[1]);
        if (isLogin)
        {
            if (passwd == pwd)
                flag = true;
            else
            {
                flag = false;
                LOG_WARN("passwd error");
            }
        }
        else
        {
            flag = false;
            LOG_WARN("user used!");
        }
    }

    if (!isLogin && flag)
    {
        LOG_DEBUG("register!");
        bzero(order, sizeof(order));
        sprintf(order, "INSERT INTO user(username, password) VALUES('%s','%s')", name.c_str(), pwd.c_str());
        if (mysql_query(sql, order))
        {
            LOG_ERROR("inser error");
            flag = false;
        }
        else
        {
            LOG_DEBUG("insert success");
            flag = true;
        }
    }
    SqlConnPool::Instance()->FreeConn(sql);
    return flag;
}

int HttpRequest::ConverHex(char ch)
{
    if (ch >= 'A' && ch <= 'F')
        return ch - 'A' + 10;
    if (ch >= 'a' && ch <= 'f')
        return ch - 'a' + 10;
    return ch;
}

std::string HttpRequest::path() const
{
    return path_;
}

std::string &HttpRequest::path()
{
    return path_;
}
std::string HttpRequest::method() const
{
    return method_;
}

std::string HttpRequest::version() const
{
    return version_;
}

std::string HttpRequest::GetPost(const std::string &key) const
{
    assert(key != "");
    if (post_.count(key) == 1)
    {
        return post_.find(key)->second;
    }
    return "";
}

std::string HttpRequest::GetPost(const char *key) const
{
    assert(key != nullptr);
    if (post_.count(key) == 1)
    {
        return post_.find(key)->second;
    }
    return "";
}